﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using ComputerArchitecture.Core;

namespace ComputerArchitecture.Numbers
{
    public static class BitNumberExtension
    {
        public static sbyte ToByte(this ISignedBitNumber number)
        {
            if (ReferenceEquals(number, null))
            {
                throw new ArgumentNullException("number");
            }
            const int size = sizeof (sbyte)*8;
            if (number.Size > size)
            {
                throw new ArgumentException();
            }
            sbyte mask = 0x01, result = 0;
            for (int i = number.Size - 1; i >= 0; --i)
            {
                if (number[i].IsSet)
                {
                    result = (sbyte)(result | mask);
                }
                mask <<= 1;
            }
            return result;
        }

        public static short ToInt16(this ISignedBitNumber number)
        {
            if (ReferenceEquals(number, null))
            {
                throw new ArgumentNullException("number");
            }
            const int size = sizeof (short)*8;
            if (number.Size > size)
            {
                throw new ArgumentException();
            }
            short mask = 0x0001, result = 0;
            for (int i = number.Size - 1; i >= 0; --i)
            {
                if (number[i].IsSet)
                {
                    result = (short)(result | mask);
                }
                mask <<= 1;
            }
            return result;
        }

        public static int ToInt32(this ISignedBitNumber number)
        {
            if (ReferenceEquals(number, null))
            {
                throw new ArgumentNullException("number");
            }
            const int size = sizeof(int) * 8;
            if (number.Size > size)
            {
                throw new ArgumentException();
            }
            int mask = 0x00000001, result = 0;
            for (int i = number.Size - 1; i >= 0; --i)
            {
                if (number[i].IsSet)
                {
                    result = result | mask;
                }
                mask <<= 1;
            }
            return result;
        }

        public static long ToInt64(this ISignedBitNumber number)
        {
            if (ReferenceEquals(number, null))
            {
                throw new ArgumentNullException("number");
            }
            const int size = sizeof(long) * 8;
            if (number.Size > size)
            {
                throw new ArgumentException();
            }
            long mask = 0x1, result = 0;
            for (int i = number.Size - 1; i >= 0; --i)
            {
                if (number[i].IsSet)
                {
                    result = result | mask;
                }
                mask <<= 1;
            }
            return result;
        }

        public static ulong ToUInt64(this IUnsignedBitNumber number)
        {
            if (ReferenceEquals(number, null))
            {
                throw new ArgumentNullException("number");
            }
            const int size = sizeof(ulong) * 8;
            if (number.Size > size)
            {
                throw new ArgumentException();
            }
            ulong mask = 0x1, result = 0;
            for (int i = number.Size - 1; i >= 0; --i)
            {
                if (number[i].IsSet)
                {
                    result = result | mask;
                }
                mask <<= 1;
            }
            return result;
        }

        public static uint ToUInt32(this IUnsignedBitNumber number)
        {
            if (ReferenceEquals(number, null))
            {
                throw new ArgumentNullException("number");
            }
            const int size = sizeof(uint) * 8;
            if (number.Size > size)
            {
                throw new ArgumentException();
            }
            uint mask = 0x1, result = 0;
            for (int i = number.Size - 1; i >= 0; --i)
            {
                if (number[i].IsSet)
                {
                    result = result | mask;
                }
                mask <<= 1;
            }
            return result;
        }

        public static SignedBitNumber CreateByteNumber(this sbyte value)
        {
            const int size = sizeof(sbyte) * 8;
            SignedBitNumber number = new SignedBitNumber(size);
            sbyte mask = 0x01;
            for (int i = size - 1; i >= 0; --i)
            {
                if ((value & mask) == 0x0)
                {
                    number[i] = Bit.Zero;
                }
                else
                {
                    number[i] = Bit.One;
                }
                mask = (sbyte)(mask << 1);
            }
            return number;
        }

        public static SignedBitNumber CreateWordNumber(this short value)
        {
            const int size = sizeof(short) * 8;
            SignedBitNumber number = new SignedBitNumber(size);
            short mask = 0x01;
            for (int i = size - 1; i >= 0; --i)
            {
                if ((value & mask) == 0x0)
                {
                    number[i] = Bit.Zero;
                }
                else
                {
                    number[i] = Bit.One;
                }
                mask = (short)(mask << 1);
            }
            return number;
        }

        public static UnsignedBitNumber ToUnsigned(this ushort value)
        {
            return CreateWordNumber(value);
        }

        public static UnsignedBitNumber CreateWordNumber(this ushort value)
        {
            const int size = sizeof(short) * 8;
            UnsignedBitNumber number = new UnsignedBitNumber(size);
            ushort mask = 0x01;
            for (int i = size - 1; i >= 0; --i)
            {
                if ((value & mask) == 0x0)
                {
                    number[i] = Bit.Zero;
                }
                else
                {
                    number[i] = Bit.One;
                }
                mask = (ushort)(mask << 1);
            }
            return number;
        }

        public static SignedBitNumber CreateQuartedWordNumber(this long value)
        {
            const int size = sizeof(long) * 8;
            SignedBitNumber number = new SignedBitNumber(size);
            long mask = 0x01;
            for (int i = size - 1; i >= 0; --i)
            {
                if ((value & mask) == 0x0)
                {
                    number[i] = Bit.Zero;
                }
                else
                {
                    number[i] = Bit.One;
                }
                mask = mask << 1;
            }
            return number;
        }

        public static SignedBitNumber CreateDoubleWordNumber(this int value)
        {
            const int size = sizeof(int) * 8;
            SignedBitNumber number = new SignedBitNumber(size);
            int mask = 0x01;
            for (int i = size - 1; i >= 0; --i)
            {
                if ((value & mask) == 0x0)
                {
                    number[i] = Bit.Zero;
                }
                else
                {
                    number[i] = Bit.One;
                }
                mask = mask << 1;
            }
            return number;
        }

        public static UnsignedBitNumber CreateQuartedWordNumber(this ulong value)
        {
            const int size = sizeof(long) * 8;
            UnsignedBitNumber number = new UnsignedBitNumber(size);
            ulong mask = 0x01;
            for (int i = size - 1; i >= 0; --i)
            {
                if ((value & mask) == 0x0)
                {
                    number[i] = Bit.Zero;
                }
                else
                {
                    number[i] = Bit.One;
                }
                mask = mask << 1;
            }
            return number;
        }

        public static UnsignedBitNumber ToUnsigned(this ISignedBitNumber signedBitNumber)
        {
            return new UnsignedBitNumber(signedBitNumber.AsEnumerable());
        }

        public static SignedBitNumber ToSigned(this IUnsignedBitNumber signedBitNumber)
        {
            return new SignedBitNumber(signedBitNumber.AsEnumerable());
        }
    }
}
